package com.example.online_search_springboot.searcher;

import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

//整个搜索过程
public class DocSearcher {
    //    /usr/sofrwares_tl/tomcat_1109/tomcat_8.5/online_search_doc
    private static String STOP_WORD_PATH = null;

    static {
        if (Config.isOnline) {
            STOP_WORD_PATH = "src/main/resources/static/online_search_doc/stop_word.txt";
        } else {
            STOP_WORD_PATH = "C:/Users/Administrator/Desktop/online_search_doc/stop_word.txt";
        }
    }

    private HashSet<String> stopWords = new HashSet<>();
    private Index index = new Index();

    //将做好的索引加载到内存中
    public DocSearcher() {
        index.load();
        loadStopWords();
    }

    public List<Result> search(String query) {
        //1.分词
        List<Term> oldTerms = ToAnalysis.parse(query).getTerms();
        List<Term> terms = new ArrayList<>();
        for (Term term : oldTerms) {
            if (stopWords.contains(term.getName())) {
                continue;
            }
            terms.add(term);
        }
        //2.触发
        List<List<Weight>> termResult = new ArrayList<>();

        for (Term term : terms) {
            String word = term.getName();
            List<Weight> invertedList = index.getInverted(word);
            if (invertedList == null) {
                //这个词在所有文档中不存在
                continue;
            }
            termResult.add(invertedList);
        }

        //针对多个分词结果触发出的相同文档，进行权重的合并。
        List<Weight> allTermTesult = mergeResult(termResult);

        //3.排序
        allTermTesult.sort(new Comparator<Weight>() {
            @Override
            public int compare(Weight o1, Weight o2) {
                return o2.getWeight() - o1.getWeight(); //降序排序
            }
        });
        //4.包装结果
        //根据倒排查找正排
        List<Result> results = new ArrayList<>();
        for (Weight weight : allTermTesult) {
            DocInfo docInfo = index.getDocInfo(weight.getDocId());
            Result result = new Result();
            result.setTitle(docInfo.getTitle());
            result.setUrl(docInfo.getUrl());
            result.setDesc(GenDesc(docInfo.getContent(), terms));
            results.add(result);
        }
        return results;
    }

    //通过这个内部类，描述一个二维数组中的位置
    static class Pos {
        public int row;
        public int col;

        public Pos(int row, int col) {
            this.row = row;
            this.col = col;
        }
    }

    private List<Weight> mergeResult(List<List<Weight>> source) {
        //在合并的时候，把多个行合并成一行了
        //合并的时候，遍历二维数组
        //1.先针对每一行进行排序（按照id升序排序）
        for (List<Weight> curRow : source) {
            curRow.sort(new Comparator<Weight>() {
                @Override
                public int compare(Weight o1, Weight o2) {
                    return o1.getDocId() - o2.getDocId();
                }
            });
        }
        //2.借助优先级队列，针对这些行进行合并
        List<Weight> target = new ArrayList<>();
        PriorityQueue<Pos> queue = new PriorityQueue<>(new Comparator<Pos>() {
            @Override
            public int compare(Pos o1, Pos o2) {
                //先根据pos值找到对应的Weight对象，在根据Weight的docId来排序
                Weight w1 = source.get(o1.row).get(o1.col);
                Weight w2 = source.get(o2.row).get(o2.col);
                return w1.getDocId() - w2.getDocId();
            }
        });

        //初始化队列，把每一行的第一个元素放到队列中
        for (int row = 0; row < source.size(); row++) {
            //初始插入元素的col就是0
            queue.offer(new Pos(row, 0));
        }

        //循环取对首元素
        while (!queue.isEmpty()) {
            Pos minPos = queue.poll();
            Weight curWeight = source.get(minPos.row).get(minPos.col);
            //看看这个取到Weight是否和前一个插入到target中的结果是相同的docId
            //如果是合并
            if (target.size() > 0) {
                Weight lastWeight = target.get(target.size() - 1);
                if (lastWeight.getDocId() == curWeight.getDocId()) {
                    //说明遇到了相同的文档
                    //合并权重
                    lastWeight.setWeight(lastWeight.getWeight() + curWeight.getWeight());
                } else {
                    //如果文档id不相同，就直接把curWeight给插入到target的末尾
                    target.add(curWeight);
                }
            } else {
                //如果target当前是空，就直接插入即可
                target.add(curWeight);
            }

            //这一行下一个元素
            Pos newPos = new Pos(minPos.row, minPos.col + 1);
            if (newPos.col >= source.get(newPos.row).size()) {
                continue;
            }
            //队列是优先级队列，会自动调整
            queue.offer(newPos);
        }
        return target;
    }

    //生成描述的方法
    private String GenDesc(String content, List<Term> terms) {
        int firstPos = -1;
        for (Term term : terms) {
            String word = term.getName();
            content = content.toLowerCase().replaceAll("\\b" + word + "\\b", " " + word + " ");
            firstPos = content.indexOf(" " + word + " ");
            if (firstPos >= 0) {
                //找到了位置
                break;
            }
        }

        if (firstPos == -1) {
            if (content.length() > 160) {
                return content.substring(0, 160) + "...";
            }
            //所有的分词结果都不在正文中存在 我们返回空的描述就不合适，所以我们返回正文的前160个字符

            return content;
        }

        //
        String desc = "";
        int descBeg = firstPos < 60 ? 0 : firstPos - 60;
        if (descBeg + 160 > content.length()) {
            desc = content.substring(descBeg);//从起始位置截到末尾
        } else {
            desc = content.substring(descBeg, descBeg + 160) + "...";
        }

        for (Term term : terms) {
            String word = term.getName();
            //此处进行全字匹配，也就是当查询词为list的时候不能把Arraylist中的List给单独标红。(?i) 不区分大小写替换
            desc = desc.replaceAll("(?i) " + word + " ", "<i> " + word + " </i>");
        }
        return desc;
    }

    public void loadStopWords() {
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(STOP_WORD_PATH))) {
            while (true) {
                String line = bufferedReader.readLine();
                if (line == null) {
                    break;
                }
                stopWords.add(line);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        DocSearcher docSearcher = new DocSearcher();
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("->");
            String query = scanner.next();
            List<Result> results = docSearcher.search(query);
            for (Result result : results) {
                System.out.println("============================");
                System.out.println(result);
            }
        }

    }
}
